﻿using AnalyStock.DataManage;
using static AnalyStock.DataManage.QueryInflectPointsExtension;

namespace AnalyStock.DataAnalysis;

internal static class SeleStockMethod
{
    /// <summary>
    ///     获得换手率值
    /// </summary>
    /// <param name="list"></param>
    /// <param name="mergeBak"></param>
    /// <returns></returns>
    public static float GetTurnOverRateOnCurrent(this IList<CandlePoint> list, StockInfo mergeBak)
    {
        return mergeBak.Float_share > 0
            ? list.Last().Vol / (mergeBak.Float_share * 10000.0f) //亿股
            : 0.0f;
    }

    public static float GetTurnOverRateOnCurrent(this float vol, StockInfo mergeBak)
    {
        return mergeBak.Float_share > 0
            ? vol / (mergeBak.Float_share * 10000.0f) //亿股
            : 0.0f;
    }

    /// <summary>
    ///     获取量比值
    /// </summary>
    /// <param name="list"></param>
    /// <returns></returns>
    public static float GetQuantityRelativerRatioOnCurrent(this IList<CandlePoint> list)
    {
        var ma5Vol = list.Count >= 6
            ? list.SkipLast(1).TakeLast(5).Sum(n => n.Vol) / 5.0f
            : list.Count >= 2
                ? list.SkipLast(1)
                    .TakeLast(list.Count - 1)
                    .Sum(n => n.Vol) / (list.Count - 1)
                : list.First().Vol;
        return list.Last().Vol / ma5Vol;
    }

    /// <summary>
    ///     取得区间的K线各类值
    /// </summary>
    /// <param name="days"></param>
    /// <param name="basicStock"></param>
    /// <returns></returns>
    public static (bool HasValue, TrendIndexInPeriod IndexValue)
        GetTrendIndexsInLastDays(this IList<CandlePoint> list, int days)
    {
        var candles = list.TakeLast(days);
        if (!candles.Any())
        {
            return (false, null);
        }

        var currCandle = candles.Last();
        var maxCandle = candles.MaxBy(n => n.Close);
        var minCandle = candles.MinBy(n => n.Close);
        var indextotal = candles.Last().LocationIndex;
        //10个交易日期红色进攻倍量
        var countDoubleVol = candles.TakeLast(10)
            .Where(n => n.IsDoubleVol && n.IsCloseUpOpen)
            .Count();
        TrendIndexInPeriod tmpindex = new()
        {
            StockCode = currCandle.StockCode,
            StockName = currCandle.Name,
            Current = currCandle.Close,
            Max = maxCandle.Close,
            Min = minCandle.Close,
            MaxIdx = maxCandle.LocationIndex,
            MinIdx = minCandle.LocationIndex,
            WaveRate = minCandle.Close / maxCandle.Close,
            RatioCurrentToMax = currCandle.Close / maxCandle.Close,
            DaysToMax = indextotal - maxCandle.LocationIndex,
            DaysToMin = indextotal - minCandle.LocationIndex,
            DoubleVolCountsIn20Days = countDoubleVol
        };
        return (true, tmpindex);
    }

    /// <summary>
    ///     取得最近的主要财务指标
    /// </summary>
    /// <param name="basicStock"></param>
    /// <returns></returns>
    public static (bool HasValue, DynamicFinancialIndex IndexValue)
        GetLastFinaIndex(this IList<DynamicFinancialIndex> list)
    {
        if (list.IsNullOrEmpty())
        {
            return (false, null);
        }

        return (true, list.Last());
    }

    /// <summary>
    ///     近期最近高拐点
    /// </summary>
    /// <param name="inflectPointOfPrices"></param>
    /// <returns></returns>
    public static (bool HasValue, TopInflectPoint IndexValue)
        GetLastTopPoint(this IList<SamplePoint> list)
    {
        var tops = list.TakePoints(InflectPType.Top);
        if (!tops?.Any() ?? true)
        {
            return (false, null);
        }

        var lastTop = tops.Last();
        var current = list.Last();
        TopInflectPoint topPoint = new()
        {
            LocationIndex = lastTop.LocationIndex,
            PointValue = lastTop.Close,
            GrowToCurrent = current.Close / lastTop.Close,
            DaysToCurrent = current.LocationIndex - lastTop.LocationIndex
        };
        //附近是否三日内有涨停
        topPoint.IsSkyRocketing = lastTop.LocationIndex switch
        {
            0 => CandlePoints[0].IsSkyrocketing,
            1 => CandlePoints[1].IsSkyrocketing
                 || CandlePoints[0].IsSkyrocketing,
            2 => CandlePoints[2].IsSkyrocketing
                 || CandlePoints[1].IsSkyrocketing
                 || CandlePoints[0].IsSkyrocketing,
            >= 3 => CandlePoints[topPoint.LocationIndex - 3].IsSkyrocketing
                    || CandlePoints[topPoint.LocationIndex - 2].IsSkyrocketing
                    || CandlePoints[topPoint.LocationIndex - 1].IsSkyrocketing
                    || CandlePoints[topPoint.LocationIndex].IsSkyrocketing,
            _ => false
        };
        return (true, topPoint);
    }

    /// <summary>
    ///     Days内Ma5和Ma20线金叉次数，最近金叉距离目前交易日
    /// </summary>
    /// <param name="withinDays"></param>
    /// <returns></returns>
    public static (int CountGoldCross, int DaysToCurrent)
        AnalyGoldCrossMa5toMa20InDays(this IList<CandlePoint> list, int withinDays)
    {
        var inSeries = list.TakeCloseArray();
        MaOfKline[0]?.Clear();
        MaOfKline[0] = inSeries.CalculateMAVal(5);
        MaOfKline[1]?.Clear();
        MaOfKline[1] = inSeries.CalculateMAVal(20);
        var countGoldCross = 0;
        var daysToCurrent = -1;
        if (MaOfKline[1].IsNullOrEmpty())
        {
            return (0, -1);
        }

        var (stratIndex, endIndex) = (MaOfKline[0].Count - 1,
            MaOfKline[0].Count > withinDays ? MaOfKline[0].Count - withinDays : 0);
        for (var i = stratIndex; i > endIndex; i--)
        {
            var valSection = (
                MaOfKline[0][i].MAValue - MaOfKline[1][i].MAValue,
                MaOfKline[0][--i].MAValue - MaOfKline[1][--i].MAValue);
            //形成金叉
            if (valSection.Item1 >= 0 && valSection.Item2 < 0)
            {
                daysToCurrent = countGoldCross++ is 0 ? MaOfKline[0].Count - i : daysToCurrent;
            }
        }

        return (countGoldCross, daysToCurrent);
    }

    /// <summary>
    ///     Days内Ma5和Ma10线金叉次数，最近金叉距离目前交易日
    /// </summary>
    /// <param name="withinDays"></param>
    /// <returns></returns>
    public static (int CountGoldCross, int DaysToCurrent)
        AnalyGoldCrossMa5toMa10InDays(this IList<CandlePoint> list, int withinDays)
    {
        var inSeries = list.TakeCloseArray();
        MaOfKline[0]?.Clear();
        MaOfKline[0] = inSeries.CalculateMAVal(5);
        MaOfKline[1]?.Clear();
        MaOfKline[1] = inSeries.CalculateMAVal(10);
        var countGoldCross = 0;
        var daysToCurrent = -1;
        if (MaOfKline[1].IsNullOrEmpty())
        {
            return (0, -1);
        }

        var (stratIndex, endIndex) = (MaOfKline[0].Count - 1,
            MaOfKline[0].Count > withinDays ? MaOfKline[0].Count - withinDays : 0);
        for (var i = stratIndex; i > endIndex; i--)
        {
            var valSection = (
                MaOfKline[0][i].MAValue - MaOfKline[1][i].MAValue,
                MaOfKline[0][--i].MAValue - MaOfKline[1][--i].MAValue);
            //形成金叉
            if (valSection.Item1 >= 0 && valSection.Item2 < 0)
            {
                daysToCurrent = countGoldCross++ is 0 ? MaOfKline[0].Count - i : daysToCurrent;
            }
        }

        return (countGoldCross, daysToCurrent);
    }


    /// <summary>
    ///     根据九宫格个价格分布判断走势
    /// </summary>
    /// <param name="inflectPoints"></param>
    /// <returns></returns>
    public static (string TrendLable, string TrendDescribe)
        AnalyTrendOfKline(this IList<SamplePoint> samples)
    {
        if (samples.IsNullOrEmpty())
        {
            return ("?", "");
        }

        var (maxClose, minClose) = samples.MaxMinFloat(n => n.Close);
        var divMaxtoMin = maxClose - minClose;
        var idxsOfSect = samples.Count / 6;
        var valSectMean = (
            samples.Take(idxsOfSect).Sum(n => n.Close) / idxsOfSect,
            samples.Skip((samples.Count - idxsOfSect) / 2).Take(idxsOfSect).Sum(n => n.Close) / idxsOfSect,
            samples.TakeLast(idxsOfSect).Sum(n => n.Close) / idxsOfSect);
        //形成价格的相对位置幅度，标准化每个股票价格波动
        var valSectPercent = (
            (valSectMean.Item1 - minClose) / divMaxtoMin,
            (valSectMean.Item2 - minClose) / divMaxtoMin,
            (valSectMean.Item3 - minClose) / divMaxtoMin);
        //标准化各区域值：价格在顶部1，中部0，底部-1
        var valSectStandard = CommCalculate.RevisedValOnStandard(valSectPercent);
        // /:上行；\\:下行；L：回调振荡；F：冲高振荡；W：振荡；V：V型回转；A：A型小调   \\:深度下调破位
        var analyTrend = valSectStandard switch
        {
            (1, 1, 1) => ("F", "顶部持续振荡"),
            (1, 1, 0) => ("L", "顶部振荡回调"),
            (1, 1, -1) => ("L \\", "顶部振荡深调"),

            (1, 0, 1) => ("V F", "顶部回调冲高"),
            (1, 0, 0) => ("V L F", "顶部回调振荡"),
            (1, 0, -1) => ("L \\", "顶部持续回调"),

            (1, -1, 1) => ("V F", "顶部深调冲高"),
            (1, -1, 0) => ("V L", "顶部深调回升"),
            (1, -1, -1) => ("L \\", "顶部深调振荡"),
            //
            (0, 1, 1) => ("F", "中部持续上行"),
            (0, 1, 0) => ("A F", "中部上行振荡"),
            (0, 1, -1) => ("A L", "中部上行回调"),

            (0, 0, 1) => ("F", "中部振荡上行"),
            (0, 0, 0) => ("W F", "中部持续振荡"),
            (0, 0, -1) => ("W L", "中部振荡下行"),

            (0, -1, 1) => ("V F", "中部下调回升"),
            (0, -1, 0) => ("V F", "中部下调振荡"),
            (0, -1, -1) => ("L", "中部持续下调"),
            //
            (-1, 1, 1) => ("F", "底部持续走高"),
            (-1, 1, 0) => ("A F", "底部走高振荡"),
            (-1, 1, -1) => ("A L", "底部走高下行"),

            (-1, 0, 1) => ("F", "底部持续走高"),
            (-1, 0, 0) => ("A F", "底部回升振荡"),
            (-1, 0, -1) => ("A L", "底部回升下行"),

            (-1, -1, 1) => ("W F", "底部振荡冲高"),
            (-1, -1, 0) => ("W F", "底部振荡回升"),
            (-1, -1, -1) => ("W L", "底部持续振荡"),
            _ => ("?", "")
        };
        return (analyTrend.Item1, $"趋势: {analyTrend.Item2}/{analyTrend.Item1}");
    }

    ///根据1，2，3阶方程的系数判断走势，1阶一次X的系数；2阶二次系数；3阶3次系数
    public static (string TrendLable, string TrendDescribe)
        AnalyTrendOfKline(this double[] paramFitting)
    {
        var valParamStandard = (
            paramFitting[0] switch { < -0.01f => -1, >= 0.01f => 1, _ => 0 },
            paramFitting[1] switch { < -0.002f => -1, >= 0.002f => 1, _ => 0 },
            paramFitting[2] switch { < -0.0005f => -1, >= 0.0005f => 1, _ => 0 });
        var analyTrend = valParamStandard switch
        {
            (1, 1, 1 or 0) => "加速上升...",
            (1, 0, 1 or 0) => "持续上升...",
            (1, 0, -1) => "持续上升/走缓",
            (1, 1, -1) => "加速上升/走缓",

            //
            (1, -1, 1 or 0) => "总体上升，顶部震荡",
            (1, -1, -1) => "总体上升，顶部持续震荡",
            //
            (-1, 1, 1 or 0) => "下降/底部振荡/逆转",
            (-1, 0, 1 or 0) => "持续下降...",
            (-1, -1, -1 or 0) => "加速下降...",
            //
            (0, _, _) => "持续振荡",
            _ => "无法判断..."
        };
        return ($"{valParamStandard.Item1},{valParamStandard.Item2},{valParamStandard.Item3}", analyTrend);
    }

    /// <summary>
    ///     分析底部波动，是否触底三次以上
    /// </summary>
    /// <param name="inflectPointOfPrices"></param>
    /// <returns></returns>
    public static (bool IsThanThreeHitBottom, int CountHitBottomLessThan10P)
        AnalyWaveOnBottomPoints(this IList<SamplePoint> list)
    {
        var bottoms = list.TakeCloseArray(InflectPType.Bottom).ToArray();
        if (bottoms.Length < 3)
        {
            return (false, 0);
        }

        var current = list.Last().Close; //近期的收盘价
        //如果股价在创新低，或者涨幅超过20%，则不形成底部振荡
        if (current / bottoms.Min() is < 0.90f or > 1.20f)
        {
            return (false, 0);
        }

        var countHitBottom = 0;
        //底部波动：每个底部点与最近的底部点比较，如果波动幅度不超过15%，认为在底部波动
        for (var i = 1; i <= bottoms.Length; i++)
        {
            var waveRateofAdjacentPoints = MathF.Abs((bottoms.Last() / bottoms[^i]) - 1.0f);
            if (waveRateofAdjacentPoints <= 0.15f)
            {
                countHitBottom++;
            }
            else
            {
                break;
            }
        }

        //底部拐点至少3次回调，可以初步认定为在底部振荡
        return (countHitBottom >= 3, countHitBottom);
    }
}